#pragma once
#include "Mesh.h"

#include <set>

class FnSurfaceGlobalParam
{
public:
	struct TransFunc{
		Scalar a, b; // rotation matrix [ a -b | b a ]
		Param t; // translation vector 
	};

	struct PatchInfo{
		Mesh::VertexHandle corners_[4]; // 00 -> 10 -> 11 -> 01
		int adj_patch_idx_[4]; // 00 -> 01 -> 11 -> 01
		TransFunc trans_func_[4];
	};

	struct CornerInfo{
		Mesh::VertexHandle corner_vtx_;
		std::set<int> nbhd_patch_idx_;
	};

public:
  FnSurfaceGlobalParam(Mesh& _mesh);
  ~FnSurfaceGlobalParam(void);

  const Mesh& mesh() const        { return  mesh_; }
  Mesh& mesh()                    { return  mesh_; }
  void setNSides(int n){ nSides = n;}


  void  init();
  void  calc();
  int   calc_with_taucs();

  void  set_side_index(int _side_idx)     { side_idx_ = _side_idx; }
  int   get_side_index() const            { return  side_idx_; }

  void  set_side_corner_vertices(const std::vector<int>& _vtx_indices);

  Scalar cot_weight(const Mesh::EdgeHandle _eh) const;

  void  set_weight_type(WeightType _type)      { weight_type_ = _type; }
  WeightType weight_type()                     { return  weight_type_; }


  Scalar cot(const Mesh::HalfedgeHandle _corner) const;

  void  export_linear_system(const std::string& _A_dat, const std::string& _b_dat);

  void  save(const std::string& _filename);
  
  void read_face_side_info(const std::string& _filename);

  void set_patch_infos();

  void swap_param_values();
  void swap_param_value(const Mesh::VertexHandle _vh);

  void update_patches();

  void update_patch_paths();
  bool get_patch_path(const Mesh::VertexHandle _start_vh, const Mesh::VertexHandle _end_vh, std::vector<Mesh::HalfedgeHandle> &path);

  void  update_edge_weights();
  Scalar cot_surface_weight(const Mesh::EdgeHandle _eh) const;
  Scalar cot_triangle(const Mesh::HalfedgeHandle _corner) const;

  // aux info
public:
  static int  nx_face_side_idx;
  static int  px_face_side_idx;
  static int  ny_face_side_idx;
  static int  py_face_side_idx;
  static int  nz_face_side_idx;
  static int  pz_face_side_idx;    


private:
  Mesh&       mesh_;
  WeightType  weight_type_;
  std::vector<CornerInfo>   corner_infos_;
  int         side_idx_;

  std::vector<PatchInfo> patch_infos_;
  		

  OpenMesh::VPropHandleT<int>          vprop_lns_idx_;     // indices in the linear system
  
  OpenMesh::VPropHandleT<Param>        vprop_param_;
  OpenMesh::FPropHandleT<int>          fprop_side_idx_;
  OpenMesh::VPropHandleT<VertexType>   vprop_type_;
  OpenMesh::EPropHandleT<EdgeType>     eprop_type_;
  OpenMesh::EPropHandleT<Scalar>	   eprop_w_;
  OpenMesh::VPropHandleT<int>          vprop_side_idx_;
  OpenMesh::EPropHandleT<bool> eprop_edge_flag_;
  OpenMesh::EPropHandleT<float> eprop_length_;
    
  double max_edge_length_;
  int nSides;

};
